﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using SE.Halligang.CsXmpToolkit;

namespace Antlers
{
	internal class ActiveView : Control
	{
		private Dictionary<DirectoryInfo, FileInfo[]> m_activeDirectories = new Dictionary<DirectoryInfo, FileInfo[]>();
		private SortedDictionary<String, Thumbnail> m_activeSet = null;
		private HashSet<Thumbnail> m_potentialSet = null;
		private Dictionary<String, Thumbnail> m_thumbnailCache = new Dictionary<String, Thumbnail>();
		private HashSet<string> m_keywords = new HashSet<string>();
		private String[] m_supportedFileTypes = { "jpg", "png", "tiff", "gif", "nef" };
		private object lock_directoriesUpdate = new object();
		private object lock_activeSet = new object();

		internal delegate void ActiveFilesUpdatedHandler(IEnumerable<Thumbnail> activeFiles);
		internal event ActiveFilesUpdatedHandler ActiveFilesUpdated;

		internal IEnumerable<String> Keywords
		{
			get { return m_keywords; }
		}

		private IEnumerable<Thumbnail> ActiveFiles
		{
			get { return m_activeSet != null ? m_activeSet.Values : null; }
		}

		internal void SetDirectory(DirectoryInfo directory, bool includeSubDirs)
		{
			if (!directory.Exists)
			{
				throw new ArgumentException(String.Format("Directory {0} does not exist", directory), "directory");
			}

			if (m_activeDirectories.ContainsKey(directory))
			{
				throw new ArgumentException(String.Format("Directory {0} is already present in the active view", directory), "directory");
			}
			
			// Create a new Dictionary instead of clearing the old one. This will give a way to determine if the directory list has changed while updating.
			m_activeDirectories = new Dictionary<DirectoryInfo,FileInfo[]>();
			AddDirectory(directory, includeSubDirs);
			EnsureUpdate();
		}

		internal bool AddKeywordFilter(string keyword)
		{
			if (m_keywords.Add(keyword))
			{
				EnsureUpdate();
				return true;
			}

			return false;
		}

		internal bool RemoveKeywordFilter(string keyword)
		{
			if (m_keywords.Remove(keyword))
			{
				EnsureUpdate();
				return true;
			}

			return false;
		}

		private void EnsureUpdate()
		{
			Dictionary<DirectoryInfo, FileInfo[]> directories = new Dictionary<DirectoryInfo, FileInfo[]>(m_activeDirectories);
			m_activeSet = new SortedDictionary<String, Thumbnail>();
			m_potentialSet = new HashSet<Thumbnail>();

			if (ActiveFilesUpdated != null)
			{
				ActiveFilesUpdated.Invoke(m_activeSet.Values);
			}

			foreach (FileInfo[] fileList in directories.Values)
			{
				foreach (FileInfo file in fileList)
				{
					Thumbnail thumbnail;

					if (!m_thumbnailCache.TryGetValue(file.FullName, out thumbnail))
					{
						m_thumbnailCache.Add(file.FullName, thumbnail = new Thumbnail(file.FullName));
					}

					if (m_keywords.Count == 0)
					{
						m_activeSet.Add(file.FullName, thumbnail);
					}
					else
					{
						m_potentialSet.Add(thumbnail);
						ThumbnailIO.PrepareResource(thumbnail, ThumbnailIO.IOType.KeywordRead, KeywordsPreparedCallback);
					}
				}
			}

			if (ActiveFilesUpdated != null)
			{
				ActiveFilesUpdated.Invoke(m_activeSet.Values);
			}
		}

		private void KeywordsPreparedCallback(Thumbnail thumbnail, ThumbnailIO.IOType type)
		{
			if (InvokeRequired)
			{
				Invoke(new ThumbnailIO.ThumbnailReadyCallback(KeywordsPreparedCallback), new object[] { thumbnail, type });
			}
			else
			{
				if (m_potentialSet.Contains(thumbnail) && thumbnail.ContainsAnyKeyword(m_keywords))
				{
					m_activeSet.Add(thumbnail.FilePath, thumbnail);
					m_potentialSet.Remove(thumbnail);
				}

				if (ActiveFilesUpdated != null)
				{
					ActiveFilesUpdated.Invoke(m_activeSet.Values);
				}
			}
		}

		private void AddDirectory(DirectoryInfo directory, bool includeSubDirs)
		{
			SortedList<String, FileInfo> files = new SortedList<String, FileInfo>();
			foreach (String extension in m_supportedFileTypes)
			{
				foreach (FileInfo file in directory.GetFiles("*." + extension))
				{
					files.Add(file.FullName, file);
				}
			}

			m_activeDirectories.Add(directory, files.Values.ToArray());

			if (includeSubDirs)
			{
				foreach (DirectoryInfo subDir in directory.GetDirectories())
				{
					if ((subDir.Attributes & (FileAttributes.System | FileAttributes.Hidden)) == 0)
					{
						AddDirectory(subDir, true);
					}
				}
			}
		}
	}
}
